package com.medicine.product.utils;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;

/**
 * 日期工具类
 *
 * @author hejian
 */
public class DateUtils {

	/**
	 * 日期格式，年份，例如：2004，2008
	 */
	public static final String DATE_FORMAT_YYYY = "yyyy";

	// ==格式到年==
	/**
	 * 日期格式，年份和月份，例如：200707，200808
	 */
	public static final String DATE_FORMAT_YYYYMM = "yyyyMM";


	// ==格式到年月 ==
	/**
	 * 日期格式，年份和月份，例如：200707，2008-08
	 */
	public static final String DATE_FORMAT_YYYY_MM = "yyyy-MM";
	/**
	 * 日期格式，年月日，例如：050630，080808
	 */
	public static final String DATE_FORMAT_YYMMDD = "yyMMdd";


	// ==格式到年月日==
	/**
	 * 日期格式，年月日，用横杠分开，例如：06-12-25，08-08-08
	 */
	public static final String DATE_FORMAT_YY_MM_DD = "yy-MM-dd";
	/**
	 * 日期格式，年月日，例如：20050630，20080808
	 */
	public static final String DATE_FORMAT_YYYYMMDD = "yyyyMMdd";

	/**
	 * 日期格式，年月日，例如：2005/06/30，2008/08/08
	 */
	public static final String DATE_FORMAT_YYYYMMDD_SLASH = "yyyy/MM/dd";

	public static final String DATE_FORMAT_YYYYMMDD_HHU_MM_SLASH = "yyyy/MM/dd HH:mm";
	/**
	 * 日期格式，年月日，用横杠分开，例如：2006-12-25，2008-08-08
	 */
	public static final String DATE_FORMAT_YYYY_MM_DD = "yyyy-MM-dd";
	/**
	 * 日期格式，年月日，例如：2016.10.05
	 */
	public static final String POINT_YYYY_MMU_DD = "yyyy.MM.dd";
	/**
	 * 日期格式，月日，例如：10.05
	 */
	public static final String POINT_MMU_DD = "MM.dd";
	/**
	 * 日期格式，年月日，例如：2016年10月05日
	 */
	public static final String CHINA_YYYY_MMU_DD = "yyyy年MM月dd日";
	/**
	 * 日期格式，年月日，例如：2016年10月05日 12:00
	 */
	public static final String CHINA_YYYYMMDD_HHU_MM = "yyyy年MM月dd日 HH:mm";
	/**
	 * 日期格式，年月日，例如：2016100512:00:99
	 */
	public static final String YYYY_MMU_DD_HHU_MM_SS1 = "yyyyMMddHH:mm:ss";
	/**
	 * 日期格式，年月日时分，例如：200506301210，200808081210
	 */
	public static final String YYYY_MMU_DD_HHU_MM = "yyyyMMddHHmm";

	// ==格式到年月日 时分 ==
	/**
	 * 日期格式，年月日时分，例如：20001230 12:00，20080808 20:08
	 */
	public static final String YYYY_MMU_DD__HHU_MM1 = "yyyyMMdd HH:mm";
	/**
	 * 日期格式，年月日时分，例如：2000-12-30 12:00，2008-08-08 20:08
	 */
	public static final String YYYY_MMU_DD__HHU_MM2 = "yyyy-MM-dd HH:mm";
	/**
	 * 日期格式，年月日时分秒，例如：20001230120000，20080808200808
	 */
	public static final String YYYY_MMU_DD_HHU_MM_SS2 = "yyyyMMddHHmmss";


	// ==格式到年月日 时分秒==
	/**
	 * 日期格式，年月日时分秒，年月日用横杠分开，时分秒用冒号分开
	 * 例如：2005-05-10 23：20：00，2008-08-08 20:08:08
	 */
	public static final String YYYY_MMU_DD__HHU_MM_SS = "yyyy-MM-dd HH:mm:ss";
	/**
	 * 日期格式，年月日时分秒毫秒，例如：20001230120000123，20080808200808456
	 */
	public static final String YYYY_MMU_DD_HHU_MM_SS_SSSU = "yyyyMMddHHmmssSSS";


	// ==格式到年月日 时分秒 毫秒==
	/**
	 * 日期格式，月日时分，例如：10-05 12:00
	 */
	public static final String MMU_DD__HHU_MM = "MM-dd HH:mm";


	// ==特殊格式==
	/**
	 * 日期格式，天/月 ,比如 :四月十二号 : 12/4
	 */
	public static final String D_M = "d/M";
	/**
	 * 时间格式 小时：分钟 ,比如 十三点十五分 ：13:15
	 */
	public static final String HHU_MM = "HH:mm";
	public final static DateTimeFormatter FORMAT =
			DateTimeFormatter.ofPattern(DateUtils.CHINA_YYYYMMDD_HHU_MM);
	public final static DateTimeFormatter FORMAT_112 =
			DateTimeFormatter.ofPattern(DateUtils.YYYY_MMU_DD__HHU_MM_SS);
	public final static DateTimeFormatter FORMAT_113 =
			DateTimeFormatter.ofPattern(DateUtils.YYYY_MMU_DD_HHU_MM_SS1);
	public final static DateTimeFormatter FORMAT_114 =
			DateTimeFormatter.ofPattern(DateUtils.DATE_FORMAT_YYYY_MM_DD);
	private static final Logger logger = LoggerFactory.getLogger(DateUtils.class);


	/* ************工具方法***************   */

	/**
	 * 格式化Date时间
	 *
	 * @param time         Date类型时间
	 * @param pattern      String类型格式
	 * @param defaultValue 默认值为当前时间Date
	 * @return 格式化后的字符串
	 */
	public static String format(ZonedDateTime time, String pattern,
	                            final ZonedDateTime defaultValue) {
		try {
			return format(time, pattern);
		} catch (Exception e) {
			if (defaultValue != null) {
				return format(defaultValue, pattern);
			} else {
				return format(ZonedDateTime.now(), pattern);
			}
		}
	}

	/**
	 * 格式化Date时间
	 *
	 * @param time         Date类型时间
	 * @param timeFormat   String类型格式
	 * @param defaultValue 默认时间值String类型
	 * @return 格式化后的字符串
	 */
	public static String format(ZonedDateTime time, String timeFormat, final String defaultValue) {
		try {
			DateTimeFormatter formatter = DateTimeFormatter.ofPattern(timeFormat);
			return formatter.format(time);
		} catch (Exception e) {
			return defaultValue;
		}
	}

	/**
	 * 格式化时间戳{@see Timestamp} 如果格式化抛出异常将会返回空串
	 *
	 * @param timestamp  时间戳
	 * @param timeFormat 格式化模板
	 * @return 返回一个格式化后的字符串
	 * @see Timestamp
	 */
	public static String format(Timestamp timestamp, String timeFormat) {
		return format(timestamp, timeFormat, StringUtils.EMPTY);
	}

	public static String format(Timestamp timestamp, String timeFormat, String defaultValue) {
		if (timestamp == null
				|| StringUtils.isEmpty(timeFormat)) {
			return StringUtils.EMPTY;
		}
		ZoneId zone = ZoneId.systemDefault();
		ZonedDateTime zonedDateTime = ZonedDateTime.ofInstant(timestamp.toInstant(),
				zone);
		return format(zonedDateTime, timeFormat, defaultValue);
	}

	/**
	 * 格式化String时间
	 *
	 * @param time          String类型时间
	 * @param formatPattern String类型格式
	 * @return 格式化后的Date日期
	 */
	public static ZonedDateTime parse(String time, String formatPattern) {
		if (StringUtils.isEmpty(time)) {
			return null;
		}

		DateTimeFormatter deteFormatter = DateTimeFormatter.ofPattern(formatPattern);
		//需要确认时区 因为使用的 ZonedDateTime 时区信息从ZoneId中拿到
		try {
			return LocalDateTime.parse(time, deteFormatter).atZone(ZoneId.systemDefault());
		} catch (Exception e) {
			logger.error(e.getMessage());
			return null;
		}
	}

	/**
	 * 格式化String时间
	 *
	 * @param time          String类型时间
	 * @param formatPattern String类型格式
	 * @return 格式化后的Date日期
	 */
	public static LocalDate parseLocalDate(String time, String formatPattern) {
		if (StringUtils.isEmpty(time)) {
			return null;
		}

		DateTimeFormatter deteFormatter = DateTimeFormatter.ofPattern(formatPattern);
		//需要确认时区 因为使用的 ZonedDateTime 时区信息从ZoneId中拿到
		try {
			return LocalDate.parse(time, deteFormatter);
		} catch (Exception e) {
			logger.error(e.getMessage());
			return null;
		}
	}

	/**
	 * 格式化String时间
	 *
	 * @param strTime       String类型时间
	 * @param formatPattern String类型格式
	 * @param defaultValue  异常时返回的默认值
	 * @return 返回格式化时间
	 */
	public static ZonedDateTime parse(String strTime, String formatPattern,
	                                  ZonedDateTime defaultValue) {
		try {
			return parse(strTime, formatPattern);
		} catch (Exception e) {
			return defaultValue;
		}
	}

	/**
	 * 格式化日期对象 通过 formatPattern
	 *
	 * @param date          日期对象
	 * @param formatPattern 格式化模板
	 * @return 返回格式化结果 如果格式化抛出异常将会返回空串
	 * @see StringUtils
	 * {@link #format(ZonedDateTime, String, String)}
	 */
	public static String format(ZonedDateTime date, String formatPattern) {
		return format(date, formatPattern, StringUtils.EMPTY);
	}

	/**
	 * 格式化日期对象 通过 formatPattern
	 *
	 * @param date       日期对象
	 * @param dateFormat 格式化日期的工具类
	 * @return 返回格式化结果
	 */
	public static String format(ZonedDateTime date, DateTimeFormatter dateFormat) {
		return dateFormat.format(date);
	}

	public static String format(String target, String fromPattern, String toPattern) {
		if (StringUtils.isEmpty(target)
				|| StringUtils.isEmpty(fromPattern)
				|| StringUtils.isEmpty(toPattern)) {
			logger.error("method signature: format(String target, String fromPattern, String " +
					"toPattern) :format 字符串=" + target + "失败!!返回原值 cause:入参有空值 目标字符串 = " + target + " fromPattern = " + fromPattern + " toPattern = " + toPattern);
			return target;
		}
		ZonedDateTime dateTime = parse(target, fromPattern);
		return format(dateTime, toPattern);
	}


	public static Calendar getCalendar(Date date) {
		if (date == null) {
			return null;
		}
		DateFormat df = DateFormat.getDateInstance();
		df.format(date);
		return df.getCalendar();
	}

	/**
	 * 获取时间的小时
	 *
	 * @param date
	 * @return
	 */
	public static Integer getHour(Date date) {
		if (date == null) {
			return null;
		}
		return getCalendar(date).get(Calendar.HOUR_OF_DAY);
	}

	/**
	 * @Description: date转为string
	 * @Author: Paul
	 * @Date: 2019/12/4/004 16:32
	 */
	public static String parseToString(Date date, String style) {
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
		simpleDateFormat.applyPattern(style);
		String str;
		if (date == null) {
			return null;
		} else {
			str = simpleDateFormat.format(date);
			return str;
		}
	}

	public static Date parseToDate(String s, String style) {
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat();
		simpleDateFormat.applyPattern(style);
		Date date;
		if (s == null || s.length() < 8) {
			return null;
		}
		try {
			date = simpleDateFormat.parse(s);
		} catch (ParseException e) {
			e.printStackTrace();
			return null;
		}
		return date;
	}

	/**
	 * 判断输入的日期是否在下周区间
	 *
	 * @return false：不是下周；true：下周
	 * @author nemowang
	 */
	public static boolean isNextWeek(Date date) {
		// 设置时间格式
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		Calendar calendar1 = Calendar.getInstance();
		Calendar calendar2 = Calendar.getInstance();
		int dayOfWeek = calendar1.get(Calendar.DAY_OF_WEEK) - 1;
		int offset1 = 1 - dayOfWeek;
		int offset2 = 7 - dayOfWeek;
		calendar1.add(Calendar.DATE, offset1 + 7);
		calendar2.add(Calendar.DATE, offset2 + 7);
		// 下周星期一
		String monday = sdf.format(calendar1.getTime());
		//System.out.println(monday);
		// 下周星期日
		String sunday = sdf.format(calendar2.getTime());
		//System.out.println(sunday);

		String format = sdf.format(date);
		try {
			Date parse = sdf.parse(format);
			Date mon = sdf.parse(monday);
			Date sun = sdf.parse(sunday);
			return parse.compareTo(mon) >= 0 && sun.compareTo(parse) >= 0;
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * 计算年龄，根据生日日期
	 *
	 * @param birDate 生日
	 * @return 年龄
	 */
	public static String calculateAge(Date birDate) {
		LocalDate today = LocalDate.now();
		Instant instant = birDate.toInstant();
		ZoneId zoneId = ZoneId.systemDefault();
		// atZone()方法返回在指定时区从此Instant生成的ZonedDateTime。
		LocalDate localDate = instant.atZone(zoneId).toLocalDate();
		return String.valueOf(ChronoUnit.YEARS.between(localDate, today));
	}

	/**
	 * 计算年龄，根据生日日期
	 *
	 * @param birDateStr 生日日期字符串
	 * @param pattern    use in format {@code birDateStr}
	 * @return 年龄
	 */
	public static String calculateAge(String birDateStr, String pattern) {
		LocalDate today = LocalDate.now();
		LocalDate localDate = parseLocalDate(birDateStr, pattern);
		if (localDate == null) {
			return StringUtils.EMPTY;
		}
		return String.valueOf(ChronoUnit.YEARS.between(Objects.requireNonNull(localDate), today));
	}

	/**
	 * 获取星期几 通过日期
	 *
	 * @param date   日期
	 * @param prefix 前缀：周，星期
	 * @return 返回星期几 example :星期一
	 */
	public static String getWeekXQ(Date date, String prefix) {
		Calendar calendar = Calendar.getInstance();
		calendar.setTime(date);
		int day = calendar.get(Calendar.DAY_OF_WEEK);
		return getWeekXQ(day, prefix);
	}

	public static String getWeekXQ(int dayOfWeek, String prefix) {
		String week = prefix;
		switch (dayOfWeek) {
			case 2:
				week += "一";
				break;
			case 3:
				week += "二";
				break;
			case 4:
				week += "三";
				break;
			case 5:
				week += "四";
				break;
			case 6:
				week += "五";
				break;
			case 7:
				week += "六";
				break;
			case 1:
				week += "日";
				break;
			default:
				break;
		}
		return week;
	}

	/**
	 * 获取星期几 通过日期
	 *
	 * @param dateStr 日期字符串
	 * @param prefix  前缀：周，星期
	 * @return 返回星期几 example :星期一
	 */
	public static String getWeekXQ(String dateStr, String pattern, String prefix) {
		Date dateTime;
		dateTime = parseToDate(dateStr, pattern);
		return dateTime == null ? StringUtils.EMPTY : getWeekXQ(dateTime, prefix);

	}

	/**
	 * 比较 {@code target} 跟{@code subject}日期字符串的大小
	 *
	 * @param target     目标日期字符
	 * @param subject    比较主体日期字符串
	 * @param subPattern {@code target}日期模板
	 * @param tarPattern {@code subject}日期模板
	 * @return 如果 {@code target}在{{@code subject}之前则返回true
	 */
	public static Boolean isBefore(String target, String tarPattern, String subject,
	                               String subPattern) {
		ZonedDateTime dateTime = parse(target, tarPattern);
		ZonedDateTime subDateTime = parse(subject, subPattern);
		return Objects.requireNonNull(dateTime).isBefore(Objects.requireNonNull(subDateTime));
	}

	/**
	 * 比较 {@code target} 跟{@code subject}日期字符串的大小
	 *
	 * @param target  目标日期字符
	 * @param subject 比较主体日期字符串
	 * @param pattern 日期模板
	 * @return 如果 {@code target}在{{@code subject}之前则返回true
	 */
	public static Boolean isBefore(String target, String subject, String pattern) {
		ZonedDateTime dateTime = parse(target, pattern);
		ZonedDateTime subDateTime = parse(subject, pattern);
		return Objects.requireNonNull(dateTime).isBefore(Objects.requireNonNull(subDateTime));
	}

	/**
	 * 计算做体检当时的年龄
	 *
	 * @param ageDate  生日日期
	 * @param thenDate 下一个体检日期
	 * @return 做体检当时的年龄
	 */
	public static String getBirthdayToAgeInfo(Date ageDate, Date thenDate, boolean ignoreDay) {
		if (ageDate == null) {
			return "";
		}
		Calendar birthday = Calendar.getInstance();//2010年10月12日，month从0开始
		birthday.setTime(ageDate);
		Calendar now = Calendar.getInstance();
		now.setTime(thenDate);
		int day = now.get(Calendar.DAY_OF_MONTH) - birthday.get(Calendar.DAY_OF_MONTH);
		int month = now.get(Calendar.MONTH) - birthday.get(Calendar.MONTH);
		int year = now.get(Calendar.YEAR) - birthday.get(Calendar.YEAR);
		//按照减法原理，先day相减，不够向month借；然后month相减，不够向year借；最后year相减。
		if (day < 0) {
			month -= 1;
			now.add(Calendar.MONTH, -1);//得到上一个月，用来得到上个月的天数。
			day = day + now.getActualMaximum(Calendar.DAY_OF_MONTH);
		}
		if (month < 0) {
			month = (month + 12) % 12;
			year--;
		}
		StringBuilder str = new StringBuilder();
		if (year > 0) {
			str.append(year).append("岁");
		}
		if (month > 0) {
			str.append(month).append("个月");
		}
		if (!ignoreDay) {
			if (day > 0) {
				str.append(day).append("天");
			}
		}
		return str.toString();
	}

	public static String getBirthdayToAgeInfo(Date ageDate) {
		return getBirthdayToAgeInfo(ageDate, new Date(), false);
	}

	public static String getBirthdayToAgeInfo(Date ageDate, boolean ignoreDay) {
		return getBirthdayToAgeInfo(ageDate, new Date(), ignoreDay);
	}

	public static String getBirthdayToAgeInfo(Date ageDate, Date thenDate) {
		return getBirthdayToAgeInfo(ageDate, thenDate, false);
	}


	/**
	 * @description: Java中传入一个时间段，取出该时间段内所有日期的集合
	 * @author: sayid
	 */
	public static List<String> findDates(String dBegin, String dEnd) throws ParseException, ParseException {
		//日期工具类准备
		DateFormat format = new SimpleDateFormat("yyyy-MM-dd");

		//设置开始时间
		Calendar calBegin = Calendar.getInstance();
		calBegin.setTime(format.parse(dBegin));

		//设置结束时间
		Calendar calEnd = Calendar.getInstance();
		calEnd.setTime(format.parse(dEnd));

		//装返回的日期集合容器
		List<String> Datelist = new ArrayList<String>();
		//将第一个月添加里面去
		Datelist.add(format.format(calBegin.getTime()));
		// 每次循环给calBegin日期加一天，直到calBegin.getTime()时间等于dEnd
		while (format.parse(dEnd).after(calBegin.getTime())) {
			// 根据日历的规则，为给定的日历字段添加或减去指定的时间量
			calBegin.add(Calendar.DAY_OF_MONTH, 1);
			Datelist.add(format.format(calBegin.getTime()));
		}

		return Datelist;
	}

	/**
	 * 获取上个月时间
	 *
	 * @param time
	 * @return
	 */
	public static String lastMouthTime(String time) {
		Date date = parseToDate(time, YYYY_MMU_DD__HHU_MM_SS);
		Calendar startLastMou = Calendar.getInstance();
		startLastMou.setTime(date);
		startLastMou.add(Calendar.MONTH, -1);
		//上个月开始时间
		Date lastMouthTime = startLastMou.getTime();
		return parseToString(date, YYYY_MMU_DD__HHU_MM_SS);
	}

	public static Long diffTime(String startTime, String endTime) {
		Date start = parseToDate(startTime, DATE_FORMAT_YYYY_MM_DD);
		Date end = parseToDate(endTime, DATE_FORMAT_YYYY_MM_DD);
		return (end.getTime() - start.getTime()) / (24 * 60 * 60 * 1000);
	}


	/**
	 * 返回的日期字符串 日期格式为 {@link DateUtils#DATE_FORMAT_YYYY_MM_DD} Time格式由入参指定
	 *
	 * @param timeStr 时间字符串 不包含 日期，否则不应该使用该方法
	 * @return 返回日期时间字符串
	 */
	public static String concatDateForTimeStr(String timeStr) {
		String nowDateString = DateUtils.parseToString(new Date(), DateUtils.DATE_FORMAT_YYYY_MM_DD) + " ";
		return nowDateString + timeStr;
	}

	public static String getAge(Date ageDate, Date thenDate) {
		if (ageDate == null) {
			return "";
		}
		Calendar birthday = Calendar.getInstance();//2010年10月12日，month从0开始
		birthday.setTime(ageDate);
		Calendar now = Calendar.getInstance();
		now.setTime(thenDate);
		int day = now.get(Calendar.DAY_OF_MONTH) - birthday.get(Calendar.DAY_OF_MONTH);
		int month = now.get(Calendar.MONTH) - birthday.get(Calendar.MONTH);
		int year = now.get(Calendar.YEAR) - birthday.get(Calendar.YEAR);
		//按照减法原理，先day相减，不够向month借；然后month相减，不够向year借；最后year相减。
		if (day < 0) {
			month -= 1;
			now.add(Calendar.MONTH, -1);//得到上一个月，用来得到上个月的天数。
			day = day + now.getActualMaximum(Calendar.DAY_OF_MONTH);
		}
		if (month < 0) {
			month = (month + 12) % 12;
			year--;
		}
		StringBuilder str = new StringBuilder();
		if (year > 0) {
			str.append(year);
		}else {
			str.append(0);
		}

		return str.toString();
	}

	//上个月1号0点
	public static String getStartMoth(){
		SimpleDateFormat format=new SimpleDateFormat(DateUtils.YYYY_MMU_DD__HHU_MM_SS);
		Calendar calendar=Calendar.getInstance();
		calendar.add(Calendar.MONTH, -1);
		calendar.set(Calendar.DAY_OF_MONTH, 1);
		calendar.set(Calendar.HOUR_OF_DAY, 00);
		calendar.set(Calendar.MINUTE, 00);
		calendar.set(Calendar.SECOND, 00);
		calendar.set(Calendar.MILLISECOND, 000);
		return format.format(calendar.getTime());
	}

	//上个月最后一天23.59点
	public static String getEndMoth(){
		SimpleDateFormat sf=new SimpleDateFormat(DateUtils.YYYY_MMU_DD__HHU_MM_SS);
		Calendar calendar=Calendar.getInstance();
		int month=calendar.get(Calendar.MONTH);
		calendar.set(Calendar.MONTH, month-1);
		calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
		calendar.set(Calendar.HOUR_OF_DAY, 23);
		calendar.set(Calendar.MINUTE, 59);
		calendar.set(Calendar.SECOND, 59);
		calendar.set(Calendar.MILLISECOND, 999);
		return sf.format(calendar.getTime());
	}

	//获取上月
	public static String getHeadMoth(){
		SimpleDateFormat form = new SimpleDateFormat("yyyy年MM月");
		Calendar calendar=Calendar.getInstance();
		calendar.add(Calendar.MONTH, -1);
		calendar.set(Calendar.DAY_OF_MONTH, 1);
		return form.format(calendar.getTime());
	}

	//获取上月
	public static String getHeadMothyyyyMM(){
		SimpleDateFormat form = new SimpleDateFormat("yyyyMM");
		Calendar calendar=Calendar.getInstance();
		calendar.add(Calendar.MONTH, -1);
		calendar.set(Calendar.DAY_OF_MONTH, 1);
		return form.format(calendar.getTime());
	}
}

